#pragma once

#include "sock.hpp"

#include <sys/epoll.h>


namespace ns_epoll
{
    using namespace ns_sock;
    const int g_port = 8080;

    class EpollServer
    {
    private:
        uint16_t port_;
        int listen_sock_;
        //epoll句柄？
        int epfd_;
    public:
        EpollServer(int port = g_port):port_(port), listen_sock_(-1), epfd_(-1)
        {}
        void InitEpollServer()
        {
            listen_sock_ = Sock::Socket();
            Sock::Bind(listen_sock_, port_);
            Sock::Listen(listen_sock_);

            epfd_ = epoll_create(128);
            if(epfd_ < 0)
            {
                std::cerr << "create epoll model err" << std::endl;
                exit(1);
            }
        }
        //revs: 传入本轮已经就绪的事件
        //nums: 有效事件的个数
        void HanderEvents(struct epoll_event revs[], int num)
        {
            //所有的遍历都是有效检测
            for(int i = 0; i < num; i++)
            {
                int sock = revs[i].data.fd;
                uint32_t events = revs[i].events;

                //读事件就绪
                if(events & EPOLLIN)
                {
                    if(sock == listen_sock_)
                    {
                        //监听sock事件就绪
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);
                        //此处的accept不会发生阻塞
                        int fd = accept(listen_sock_, (struct sockaddr*)&peer, &len);
                        if(fd < 0)
                        {
                            std::cerr << "accept err" << std::endl;
                            continue;
                        }
                        //这里fd能直接读取吗？绝对不能！
                        //只有epoll才最清楚底层是否有数据
                        struct epoll_event ev;
                        ev.data.fd = fd;
                        ev.events = EPOLLIN;

                        epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev);

                        std::cout << "获取新链接成功，sock：" << fd << std::endl;
                        //在新的fd添加到epoll内的红黑树中，底层会自动设置回调机制
                        //对select or poll 这里需要把fd添加到数组
                        //当时需要我们手动管理的fd，现在变成了由OS自动帮我进行管理
                    }
                    else 
                    {
                        //普通的读事件就绪
                        char buffer[1024];
                        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
                        if(s > 0)
                        {
                            buffer[s] = 0;
                            std::cout << "client say# " << buffer << std::endl;
                            //添加关心写入事件
                            //为什么不直接调用write？send？不能！不能保证写入条件就绪！
                            //只有epoll本身最清楚底层是否有数据
                            struct epoll_event ev;
                            ev.data.fd = sock;
                            ev.events = EPOLLIN | EPOLLOUT;
                            epoll_ctl(epfd_, EPOLL_CTL_MOD, sock, &ev);
                        }
                        else if (s == 0)
                        {
                            std::cout << "client quit --- sock: " << sock << std::endl;
                            close(sock);
                            epoll_ctl(epfd_, EPOLL_CTL_DEL, sock, nullptr);
                        }
                        else 
                        {
                            std::cerr << "recv err" << std::endl;
                            close(sock);
                            epoll_ctl(epfd_, EPOLL_CTL_DEL, sock, nullptr);
                            exit(2);
                        }
                    }
                }
                //写事件就绪
                if(events & EPOLLOUT)
                {
                    std::string mes = "Ok!";
                    send(sock, mes.c_str(), mes.size(), 0);
                    //取消对写事件的关心，读取时间是常关心，写入事件是按需关心
                    struct epoll_event ev;
                    ev.data.fd = sock;
                    ev.events = EPOLLIN;
                    epoll_ctl(epfd_, EPOLL_CTL_MOD, sock, &ev);
                }
            }
        }
        void Loop() 
        {
            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = listen_sock_;
            //首先将唯一的网络sock，listen_sock添加到epoll中（红黑树）
            epoll_ctl(epfd_, EPOLL_CTL_ADD, listen_sock_, &ev);
            #define EV_NUM 10
            struct epoll_event revs[EV_NUM];
            int timeout = -1;
            while(true)
            {
                int num = epoll_wait(epfd_, revs, EV_NUM, timeout);
                switch (num)
                {
                case 0:
                    std::cout << "timeout..." << std::endl;
                    break;
                case -1:
                    std::cout << "epoll err" << std::endl;
                    break;
                default:
                    HanderEvents(revs, num);
                    break;
                }
            }
        }
        ~EpollServer() 
        {
            if(listen_sock_ >= 0)   close(listen_sock_);
            if(epfd_ >= 0)  close(epfd_);
        }
    };
}
